using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

using Sce.PlayStation.HighLevel.UI;

namespace VitaReader
{	
	public class ReaderData : IReaderData
	{
		private IReader mOwner;
		private String mListFile;
		private String mBookPath;
		
		private List<BookInfo> mBookList;
		private Dictionary<String, BookInfo> mBookRegistry;
		
		private bool	mNeedSaveData;
		
		private String	mLastFile;
		private BookInfo mCurrentBook;
		private String	mFileContent;
		
		//listSaveFile is the file(fullpath) in which the generated file list to be saved
		//fileListPath is where to find text files
		public ReaderData (IReader reader,String fileListPath,String listSaveFile = null)
		{
			mOwner = reader;
			mNeedSaveData = false;
			mListFile = listSaveFile;
			mBookPath = fileListPath;
		}
		
		#region books property
		//list file path property
		String IReaderData.ListFile		{get {return mListFile;}}
		
		String IReaderData.BookPath		{get {return mBookPath;}}
		
		List<BookInfo> IReaderData.BookList	{get {return mBookList;}}
		
		Dictionary<String, BookInfo> IReaderData.BookRegistry {	get {return mBookRegistry;}	}
		
		
		BookInfo IReaderData.CurrentBook
		{
			get {return mCurrentBook;}
			set {mCurrentBook = value; mNeedSaveData = true;}
		}
		
		String IReaderData.LastReadBook	{get {return mLastFile;}set {mLastFile = value;mNeedSaveData = true;}}
		#endregion
		
		//
		void IReaderData.Initialize()
		{
			if( mBookPath == null )
				return;
			
			bool needRefresh = false;
			if( mListFile != null && File.Exists(mListFile) )
			{
				mBookList = new List<BookInfo>();
				mBookRegistry = new Dictionary<String, BookInfo>();
				StreamReader reader = File.OpenText( mListFile );
				
				mLastFile = reader.ReadLine();
				while( !reader.EndOfStream )
				{
					String infoString = reader.ReadLine();
					String[] infos = infoString.Split(',');
					BookInfo info;
					
					int word = 0;
					info.FullPath = infos[word++];
					info.FileName = infos[word++];
					info.Title = infos[word++];
					info.Size = Convert.ToInt64(infos[word++]);
					
					info.LastPos = Convert.ToInt32(infos[word++]);
					//info.LastPos = 0;
					//info.CurrentPos = Convert.ToInt64(infos[word++]);
					info.BookMarks = null;
					int  bookMarkCount = Convert.ToInt32(infos[word++]);
					if( bookMarkCount != 0 )
					{
						info.BookMarks = new List<int>();
						for(int i = 0; i < bookMarkCount; ++i)
						{
							int pos  = Convert.ToInt32(infos[word++]);
							info.BookMarks.Add(pos);
						}
					}
					
					mBookList.Add( info );
					mBookRegistry.Add( info.FileName, info);
				}
			}
			else
			{
				mLastFile = "";
				needRefresh = true;
			}
			
			//save list file
			if( needRefresh )
			{
				//no cached list, directly refresh
				(this as IReaderData).RefreshBookInfo();
			}
		}
		
		//
		void IReaderData.Shutdown()
		{
			if( mBookRegistry == null )
				return;
				
			//write book list including bookmarks for each book
			if( mNeedSaveData && mListFile != null)
			{
				StringBuilder sb = new StringBuilder(10240);
				sb.Append( mLastFile );
				sb.Append( '\n' );
				foreach(KeyValuePair<String,BookInfo> elem in mBookRegistry)
				{
					BookInfo info = elem.Value;
					sb.Append(info.FullPath);
					sb.Append(',');
					sb.Append(info.FileName);
					sb.Append(',');
					sb.Append(info.Title);
					sb.Append(',');
					sb.Append( Convert.ToString(info.Size));
					sb.Append(',');
					sb.Append( Convert.ToString(info.LastPos) );
					sb.Append(',');
					if( info.BookMarks == null )
						sb.Append('0');
					else
					{
						sb.Append( Convert.ToString(info.BookMarks.Count) );
						foreach(int i in info.BookMarks)
						{
							sb.Append(',');
							sb.Append( Convert.ToString(i) );
						}
					}
					sb.Append('\n');
				}		
				File.WriteAllText(mListFile, sb.ToString(), Encoding.Unicode );
			}
		}
		
		//
		void IReaderData.RefreshBookInfo()
		{
			/*
			mBookList = new List<String>(
				//Directory.EnumerateFiles( mBookPath, "*.txt" )	//not working??
				//Directory.EnumerateFileSystemEntries( mBookPath, "*.txt" ) //not working too ?
				Directory.GetFiles( mBookPath, "*.txt" )
			                             );
			*/
			
			if( mBookList != null )
				mBookList.Clear();
			
			char[] slash = {'/', '\\'};
			char[] line_end = { '\n', '\r' };
			String[] files = Directory.GetFiles( mBookPath, "*.txt" );
			
			if( files == null )
			{
				if(mBookRegistry != null )
					mBookRegistry.Clear();
				return;
			}
			
			mBookList = new List<BookInfo>();
			Dictionary<String, BookInfo> newRegistry = new Dictionary<String, BookInfo>();
			
			for(int i = 0; i < files.Length; ++i)
			{
				String file = files[i];
				BookInfo info;
				info.FullPath = file;
			
				//we only want file names without path info
				int index = file.LastIndexOfAny(slash);
				
				if( index != -1 )
					info.FileName = file.Substring( index+1 );
				else
					info.FileName = file;
				StreamReader reader = File.OpenText( file );
				info.Size = reader.BaseStream.Length;
				
				BookInfo existing;
				if( mBookRegistry != null && ( this as IReaderData).GetBookInfo(info.FileName,out existing) )
				{
					info.BookMarks = existing.BookMarks;
					info.LastPos = existing.LastPos;
					//info.CurrentPos = existing.CurrentPos;
				}
				else
				{
					info.BookMarks = null;
					info.LastPos = 0;
					//info.CurrentPos = 0;
				}

				//guessing the title from the first line
				info.Title = "";
				do
				{
					String firstLine = reader.ReadLine();
					if( firstLine == null )
						break;
					foreach(String word in firstLine.Split( line_end ) )
					{
						String realWord = word.Trim();
						if( !realWord.Equals("") )
						{
							info.Title = realWord;
							break;
						}
					}
					
				}while( info.Title.Equals("") );
				
				mBookList.Add( info );
				newRegistry.Add( info.FileName, info);
			}
			
			mBookRegistry.Clear();
			mBookRegistry = newRegistry;
			mNeedSaveData = true;
		}
		
		//
		bool	IReaderData.DeleteBook(String fileName)
		{
			BookInfo info;
			info.BookMarks = null;
			info.FileName = null;
			info.FullPath = null;
			info.LastPos = 0;
			info.Size = 0;
			info.Title = null;
			if( (this as IReaderData).GetBookInfo(fileName,out info) )
			{
				File.Delete(info.FullPath);
				if( (this as IReaderData).LastReadBook == fileName )
					(this as IReaderData).LastReadBook = "";
				return true;
			}
			else
				return false;
		}
		
		//
		bool	IReaderData.OpenFile(String fileName,out BookInfo info)
		{
			bool found = false;
			if( (this as IReaderData).GetBookInfo(fileName,out info) )
			{
				if( File.Exists( info.FullPath ) )
				   found = true;
			}
			
			if( found )
			{
				mLastFile = fileName;
				mCurrentBook = info;
				StreamReader reader = new StreamReader( info.FullPath );
				StringBuilder sb = new StringBuilder( (int)mCurrentBook.Size );
				//mFileContent = new string('\0',mCurrentBook.Size);
				
				int index = 0;
				while( !reader.EndOfStream )
				{
					string s = reader.ReadLine();
					//for(int i = 0; i < s.Length; ++i)
					//	mFileContent[index+i] = s[i];
					//mFileContent[index + s.Length] = '\n';
					sb.Append(s);
					sb.Append('\n');
					index += s.Length;
				}
				reader.Close();
				reader.Dispose();
				reader = null;
				mFileContent = sb.ToString();
				sb.Clear();
			}
			return found;
		}
		
		bool	IReaderData.OpenHelp(String fileName,out BookInfo info)
		{
			String path = "/Application/help/"+fileName;

			info.BookMarks = null;
			info.FileName = fileName;
			info.FullPath = "";
			info.LastPos = 0;
			info.Size = 0;
			info.Title = "";
			
			if( File.Exists(path) )
			{	
				StreamReader reader = new StreamReader( path );
				StringBuilder sb = new StringBuilder( 4096 );
				//mFileContent = new string('\0',mCurrentBook.Size);
				
				int index = 0;
				while( !reader.EndOfStream )
				{
					string s = reader.ReadLine();
					//for(int i = 0; i < s.Length; ++i)
					//	mFileContent[index+i] = s[i];
					//mFileContent[index + s.Length] = '\n';
					sb.Append(s);
					sb.Append('\n');
					index += s.Length;
				}
				reader.Close();
				reader.Dispose();
				reader = null;
				mFileContent = sb.ToString();
				sb.Clear();
				return true;
			}
			else
				return false;
		}
		
		//
		void	IReaderData.CloseFile()
		{
			if( mCurrentBook.FileName != null )
			{
				mBookRegistry[ mCurrentBook.FileName ] = mCurrentBook;
				mFileContent = null;
				
				mNeedSaveData = true;
			}
		}
		
		//
		ReadResult	IReaderData.ReadForwardFile(ref PagePos pagePos, ref Label text)
		{
			//check for opened file info
			if( /*mCurrentBook.FileName == null || mCurrentBook.FullPath == null ||*/ mFileContent == null )
				return ReadResult.ERROR;
						
			//check the bounds
			if( pagePos.Start >= mFileContent.Length )
			{
				//text = "";
				//text.Text = "";
				return ReadResult.EOF;
			}
			
			//read datas
			int pos = pagePos.Start;
			pagePos.LinePos = new List<int>();
			
			int fontHeight = mOwner.View.GetFontHeight();
			StringBuilder sb = new StringBuilder(4096);
			//FileStream stream = new FileStream( mCurrentBook.FullPath, FileMode.Open, FileAccess.Read);
			//stream.Seek(pos, SeekOrigin.Begin);
			//const int GBK_CODE_PAGE = 936;
			//StreamReader reader = new StreamReader( fs/*, Encoding.GetEncoding(GBK_CODE_PAGE)*/ );		
			int lineCount = 0;
			int viewHeight = mOwner.View.Height;
			int viewWidth = mOwner.View.Width;
			int fontSize = mOwner.FontSize;
						
			while( pos < mFileContent.Length )
			{
				pagePos.LinePos.Add( pos );
				int lineWidth = 0;
				while( pos < mFileContent.Length )
				{
					char onechar = mFileContent[pos];
					++pos;
					
					if( onechar == '\n' )
					{
						++lineCount;
						break;
					}
					sb.Append( onechar );
					lineWidth += mOwner.View.GetCharWidth( onechar );
					if( lineWidth + fontSize > viewWidth )
					{
						if( mFileContent[pos] == '\n' )
							++pos;
						
						++lineCount;
						break;
					}
				}
				sb.Append('\n');
				
				if( (lineCount+1)* fontHeight > viewHeight )
					break;
			}
			
			text.Text = sb.ToString();
			sb.Clear();
			
			pagePos.Size = pos - pagePos.Start;
			return ReadResult.OK;
		}
		
		ReadResult 	IReaderData.ReadBackwardFile(ref PagePos pagePos, ref Label text)
		{
			//check for opened file info
			if( /*mCurrentBook.FileName == null || mCurrentBook.FullPath == null ||*/
			   mFileContent == null )
				return ReadResult.ERROR;
			
			//if( !File.Exists( info.FullPath ) )
			//	return false;
			
			//check the bounds
			if( pagePos.Start >= mFileContent.Length )
				pagePos.Start = mFileContent.Length-1;
			else if( pagePos.Start <= 0 )
			{
				pagePos.Start = 0;
				//text = "";
				//text.Text = "";
				//return true;
				return ReadResult.EOF;
			}
			
			//read datas
			int pos = pagePos.Start;
			pagePos.LinePos = new List<int>();
			
			//float height = 0;
			int fontHeight = mOwner.View.GetFontHeight();
			StringBuilder sb = new StringBuilder(4096);
			int viewHeight = mOwner.View.Height;
			int viewWidth = mOwner.View.Width;
			int fontSize = mOwner.FontSize;
			
			int lineCount = 0;
			int lastPos = pos;
			int lineWidth = 0;
						
			//move backwards to find the possible beginning,
			while( pos > 0 )
			{
				while( pos > 0 )
				{
					if( mFileContent[pos] == '\n' )
					{				
						lineWidth = 0;
						
						if( pos != pagePos.Start)
							++lineCount;
						
						if((lineCount)*fontHeight <= viewHeight )
							lastPos = pos;
						--pos;
						break;
					}
					else
					{
						lineWidth += mOwner.View.GetCharWidth( mFileContent[pos--]);
						
						if( lineWidth + fontSize > viewWidth )
						{
							if( mFileContent[pos] == '\n' )
								--pos;
							
							lineWidth = 0;
							++lineCount;
							break;
						}
					}
				}
				
				if( lineCount*fontHeight > viewHeight )
					break;
			}
			//if(pos > 0 )
			//	++lastPos;
			
			//move forward to format the viewer line 
			lineCount = 0;
			int currentPos = lastPos+1;
			int lastLinePos = currentPos;
			while( currentPos <= pagePos.Start )
			{
				pagePos.LinePos.Add( currentPos );
				lineWidth = 0;
				while( currentPos <= pagePos.Start  )
				{
					char onechar = mFileContent[currentPos];
					++currentPos;
					if( onechar == '\n' )
					{
						++lineCount;
						break;
					}
					
					sb.Append( onechar );
					lineWidth += mOwner.View.GetCharWidth( onechar );
					
					if( lineWidth + fontSize > viewWidth )
					{
						if( mFileContent[currentPos] == '\n' )
							++currentPos;
						++lineCount;
						break;
					}
				}
				
				if(currentPos <= pagePos.Start)
					sb.Append('\n');
				
				//if( (lineCount+1)* fontHeight > mOwner.View.Height )
				//	break;
			}
			//check the last original line(last line from back, first line from front)
			if( (lineCount+1)*fontHeight <= viewHeight )
			{
				//find the begin of line
				while( pos > 0 && mFileContent[pos] != '\n' )
					--pos;
				
				//break the original line into viewer lines according current view and only add the last viewer line
				lineCount = 0;
				lastLinePos = pos;
				while(pos < lastPos )
				{
					lineWidth = 0;
					while(pos < lastPos )
					{
						if( mFileContent[pos] == '\n' )
						{
							++pos;
							++lineCount;
							if( pos < lastPos-1 )
								lastLinePos = pos-1;
							break;
						}
						else 
						{
							lineWidth += mOwner.View.GetCharWidth( mFileContent[pos++] );
							
							if( lineWidth + fontSize > viewWidth )
							{
								if( mFileContent[pos] == '\n' )
									++pos;
								
								++lineCount;
								if( pos < lastPos-1 )
									lastLinePos = pos-1;
								break;
							}
						}
					}
				}
				
				if( lastLinePos == 0 )
				{
					pagePos.LinePos.Insert(0, lastLinePos );
					currentPos = lastPos;
					while(currentPos >= lastLinePos)
					{
						sb.Insert(0, mFileContent[currentPos--] );
					}
				}	
				else
				{
					pagePos.LinePos.Insert(0, lastLinePos+1 );
					currentPos = lastPos;
					while(currentPos > lastLinePos)
					{
						sb.Insert(0, mFileContent[currentPos--] );
					}
					++lastLinePos;
				}
			}
			
			text.Text = sb.ToString();
			sb.Clear();
			
			pagePos.Size = pagePos.Start - lastLinePos + 1;
			pagePos.Start = lastLinePos;
			return ReadResult.OK;
		}
	
		//
		bool	IReaderData.GetBookInfo(String fileName, out BookInfo info)
		{
			return mBookRegistry.TryGetValue(fileName, out info);
		}
		
		bool	IReaderData.GetBookText(int pos,int count,ref Label text)
		{
			if( mFileContent == null )
				return false;
			
			if( pos >= mFileContent.Length )
				text.Text = "";
			
			if( pos + count > mFileContent.Length )
				count = mFileContent.Length - pos;
			
			text.Text = mFileContent.Substring(pos,count)+"...";
			return true;
		}
		
	}//class
	
}//namespace

